/*
Copyright (c) 2025 Huawei Technologies Co., Ltd.
This file is a part of the CANN Open Software.
Licensed under CANN Open Software License Agreement Version 1.0 (the "License").
Please refer to the License for details. You may not use this file except in compliance with the License.
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
See LICENSE in the root of the software repository for the full text of the License. 
*/

#pragma once
#include <fstream> // ReadFile WriteFile
#include <string>
#include <sys/stat.h> // ReadFile WriteFile
#include <fcntl.h> // WriteFile O_RDWR
#include <unistd.h> // unix下头文件，提供read、write等原型
#include <cstdio> // 标准输入输出 printf fprintf
#include <algorithm>
#include "acl/acl.h"
#include "kernel_operator.h"


#define INFO_LOG(fmt, args...) fprintf(stdout, "[INFO] " fmt "\n",  ##args)
#define WARN_LOG(fmt, args...) fprintf(stdout, "[WARN] ( at line %d in func %s in file %s ) " fmt "\n", __LINE__, __func__, __FILE__, ##args)
#define ERROR_LOG(fmt, args...) fprintf(stdout, "[ERROR] ( at line %d in func %s in file %s ) " fmt "\n", __LINE__, __func__, __FILE__, ##args)

#define ACL_CHECK(x)                                \
    do{                                             \
        auto ret = (x);                             \
        if ( ret != ACL_SUCCESS ) {                 \
            ERROR_LOG("ACL_CHECK failed: "#x);      \
            exit(ret);                               \
        } else {                                    \
                                                     \
        }                                           \
    }while( 0 ) 
    

/**
 * @brief Read data from file （来自AscendC官方samples）
 * @param [in] filePath: file path
 * @param [in] buffer: file buffer for data
 * @param [out] fileSize: file (buffer) size
 * @return read result succeed or not
 */
bool ReadFile(const std::string &filePath, void *buffer, size_t bufferSize)
{
    struct stat sBuf;
    int fileStatus = stat(filePath.data(), &sBuf);
    if (fileStatus == -1) {
        ERROR_LOG("%s Failed to get file.", filePath.c_str());
        return false;
    }
    if (S_ISREG(sBuf.st_mode) == 0) {
        ERROR_LOG("%s is not a file, please enter a file", filePath.c_str());
        return false;
    }

    std::ifstream file;
    file.open(filePath, std::ios::binary);
    if (!file.is_open()) {
        ERROR_LOG("Open file failed. path = %s", filePath.c_str());
        return false;
    }

    std::filebuf *buf = file.rdbuf();
    size_t size = buf->pubseekoff(0, std::ios::end, std::ios::in);
    if (size == 0) {
        ERROR_LOG("file size is 0");
        file.close();
        return false;
    }
    if (size != bufferSize) {
        ERROR_LOG("file size is not equal buffer size, file size: %zu, while buffer size: %zu", size, bufferSize);
        file.close();
        return false;
    }
    buf->pubseekpos(0, std::ios::in);
    buf->sgetn(static_cast<char *>(buffer), size);
    file.close();
    return true;
}

/**
 * @brief Write data to file （来自AscendC官方samples）
 * @param [in] filePath: file path
 * @param [in] buffer: data to write to file
 * @param [in] size: size to write
 * @return write result
 */
bool WriteFile(const std::string &filePath, const void *buffer, size_t size)
{
    if (buffer == nullptr) {
        ERROR_LOG("Write file failed. buffer is nullptr");
        return false;
    }

    int fd = open(filePath.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWRITE);
    if (fd < 0) {
        ERROR_LOG("Open file failed. path = %s", filePath.c_str());
        return false;
    }

    size_t writeSize = write(fd, buffer, size);
    (void)close(fd);
    if (writeSize != size) {
        ERROR_LOG("Write file Failed.");
        return false;
    }

    return true;
}

/**
 * @brief 比较输出结果与期望结果，输出错误指标
 * @param [in] outputData: 算子输出结果
 * @param [in] expectData: 脚本计算的期望结果
 * @param [in] len: 数据总长（当作一维数据来计算）
 * @param [in] calc_times: 用于确定允许误差的范围（外面传入K）
 * @return: None
 */
template<typename T>
uint32_t calOutputError( T *outputData, T *expectData, uint64_t len, int64_t zeroPaddingK, int64_t zeroPaddingM, int64_t zeroPaddingN ) {
    double error_threshold, eb_threshold;
    if (std::is_same<T, float>::value) {
        if (zeroPaddingK < 2048)
            error_threshold = 1.0f/(1<<11);
        else if (zeroPaddingK < 16384)
            error_threshold = 1.0f/(1<<10);
        else
            error_threshold = 1.0f/(1<<9);
        eb_threshold = 1.0f/(1<<14);
    } else if (std::is_same<T, half>::value) {
        if (zeroPaddingK < 2048)
            error_threshold = 1.0f/(1<<8);
        else
            error_threshold = 1.0f/(1<<7);
        eb_threshold = 1.0f/(1<<10);
    }
    double max_abs_error = 0.0f;
    double max_rel_error = 0.0f;
    double sum_abs_error = 0.0f;
    double sum_rel_error = 0.0f;
    uint64_t errorCount = 0;
    double sum_eb = 0.0;

    bool errorCountWithin16 = true;

    for( uint64_t i = 0; i < len; i++ ) {
        T cur_output_data = outputData[i];
        T cur_expect_data = expectData[i];

        uint64_t singleCStride = zeroPaddingM * zeroPaddingN;
        uint64_t batchNum = i/singleCStride;
        uint64_t indexM = i%singleCStride / zeroPaddingN;
        uint64_t indexN = i%singleCStride % zeroPaddingN;


        double cur_abs_error = std::abs( static_cast<double>(cur_output_data) - static_cast<double>(cur_expect_data) );
        double cur_rel_error = cur_abs_error / ( std::abs( static_cast<double>(cur_expect_data) ) + 1e-7 );
        if ( cur_abs_error > max_abs_error ) {
            max_abs_error = cur_abs_error;
        }
        if ( cur_rel_error > max_rel_error ) {
            max_rel_error = cur_rel_error;
        }

        sum_abs_error += cur_abs_error;
        sum_rel_error += cur_rel_error;
        sum_eb += ( cur_output_data - cur_expect_data ) / std::max( std::abs( static_cast<double>(cur_expect_data)), 1.0 );
        
        if( std::abs(static_cast<double>(cur_expect_data))>=1 ){ // cur_abs_error >= cur_rel_error
            if(cur_rel_error > error_threshold){
                ++errorCount;
                if(errorCount < 16){
                    ERROR_LOG("Rel error %lf at batch %ld index (%ld, %ld). "
                            "Current data %lf and expect data %lf while error threshold %lf. ", 
                            cur_rel_error, batchNum, indexM, indexN, 
                            cur_output_data, cur_expect_data, error_threshold);
                }else{
                    if( errorCountWithin16 ){
                        ERROR_LOG("Error num exceeds 16. Stop printing error infos.");
                        errorCountWithin16 = false;
                    }
                }
            }
        }else{
            if(cur_abs_error > error_threshold){ // cur_abs_error < cur_rel_error
                ++errorCount;
                if(errorCount < 16){
                    ERROR_LOG("Abs error %lf at batch %ld index (%ld, %ld). "
                            "Current data %lf and expect data %lf while error threshold %lf. ", 
                            cur_abs_error, batchNum, indexM, indexN, 
                            cur_output_data, cur_expect_data, error_threshold);
                }else{
                    if( errorCountWithin16 ){
                        ERROR_LOG("Error num exceeds 16. Stop printing error infos.");
                        errorCountWithin16 = false;
                    }
                }
            }
        }
    }

    double avg_abs_error = sum_abs_error / static_cast<double>(len);
    double avg_rel_error = sum_rel_error / static_cast<double>(len);
    double errorCountRatio = static_cast<double>(errorCount) / static_cast<double>(len);
    double avg_eb = sum_eb / static_cast<double>(len);
    
    if( std::abs( avg_eb ) > eb_threshold ) {
        ERROR_LOG("Eb error %lf while eb threshold %lf. ", avg_eb, eb_threshold);
    }
    printf("===================================\n"
            "Output error statistics: \n"
            "===================================\n"
            "errorCount = %ld, \n"
            "errorCountRatio = %lf \n"
            "===================================\n",
            errorCount, 
            errorCountRatio
            );
    return errorCount; 
}

/**
 * @brief val 向上按照 align 的倍数取整
 * @param [in] T val：需要取整的数
 * @param [in] align：需要向上取整的最小粒度
 * @return T 向上对齐后的值
 */
template<typename T>
T RoundUp (const T val, const T align) {
    if ( align == 0 ) {
        return 0;
    } else {
        return ( val + align - 1 ) / align * align ;
    }
}



/**
 * @brief val 向下按照 align 的倍数取整
 * @param [in] T val：需要取整的数
 * @param [in] T align：需要向下取整的最小粒度
 * @return T 向下对齐后的数
 */
template<typename T>
T RoundDown ( const T val, const T align ) {
    if ( align == 0 ) {
        return 0;
    } else {
        return val / align * align;
    }
}

/**
 * @brief val 除以 divisor 向上取整 
 * @param [in] T val：被除数
 * @param [in] T divisor：除数
 * @return T val除以divisor向上取整的结果
 */
template<typename T>
T CeilDiv ( const T val, const T divisor ){
    if ( divisor == 0 ) {
        return 0;
    } else {
        return ( val + divisor - 1 ) / divisor;
    }

}



